package com.niiwoo.civet.account.service.local.openAccount2.service;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.niiwoo.activity.stage.dto.request.notice.NoviceTaskRecordRequestDTO;
import com.niiwoo.activity.stage.enums.activity.NoviceTaskPrizeEnum;
import com.niiwoo.civet.account.constant.AccountRegisterErrorCode;
import com.niiwoo.civet.account.dao.entity.OpenAccountOrder;
import com.niiwoo.civet.account.dao.entity.OpenAccountRecord;
import com.niiwoo.civet.account.dao.mapper.OpenAccountRecordMapperExt;
import com.niiwoo.civet.account.enums.AccountTypeEnum;
import com.niiwoo.civet.account.enums.OpenAccountMajorWay;
import com.niiwoo.civet.account.enums.OpenAccountRecordStatusEnum;
import com.niiwoo.civet.account.enums.OpenAccountWayEnum;
import com.niiwoo.civet.account.service.local.openAccount2.account.AccountInfoComposite;
import com.niiwoo.civet.account.service.local.openAccount2.dto.*;
import com.niiwoo.civet.account.service.local.openAccount2.lanmao.OpenAccountLanMaoHandler;
import com.niiwoo.civet.account.service.local.openAccount2.listener.OpenAccountListeners;
import com.niiwoo.civet.account.service.local.openAccount2.order.OpenAccountOrderHandler;
import com.niiwoo.civet.account.service.local.openAccount2.order.OpenAccountOrderOperation;
import com.niiwoo.civet.account.service.local.openAccount2.type.OpenAccountTypeable;
import com.niiwoo.civet.base.constant.OrgConstant;
import com.niiwoo.civet.trade.constant.RabbitConstant;
import com.niiwoo.civet.user.constants.UserRabbitConstant;
import com.niiwoo.civet.user.dto.CallOutTaskAddReqDTO;
import com.niiwoo.tripod.lanmao.enums.UserRoleEnum;
import com.niiwoo.tripod.lanmao.response.LanMaoGatewayDTO;
import com.niiwoo.tripod.provider.exception.BizException;
import com.niiwoo.tripod.service.component.SnowflakeIdWorker;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionSynchronizationAdapter;
import org.springframework.transaction.support.TransactionSynchronizationManager;

import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Optional;

@Slf4j
public abstract class AbstractOpenAccountService<T> implements OpenAccountTypeable<T> {

    @Autowired
    protected OpenAccountOrderHandler openAccountOrderHandler;

    @Autowired
    protected OpenAccountListeners openAccountListeners;

    @Autowired
    protected SnowflakeIdWorker snowflakeIdWorker;

    @Autowired
    protected AccountInfoComposite accountInfoComposite;

    @Autowired
    protected OpenAccountRecordMapperExt openAccountRecordMapperExt;

    @Autowired
    protected OpenAccountLanMaoHandler openAccountLanMaoHandler;

    @Autowired
    protected RabbitTemplate rabbitTemplate;

    @Transactional(isolation = Isolation.READ_COMMITTED, rollbackFor = Exception.class)
    public LanMaoGatewayDTO create(CreateRequest request) {
        this.innerCreateParamValid(request);
        // 查询开户记录
        List<OpenAccountRecord> recordList = Optional.ofNullable(openAccountRecordMapperExt.selectByUserIdAndOrgId(request.getUserId(),request.getOrgId())).orElse(Collections.emptyList());
        // 当前角色开户记录
        Optional<OpenAccountRecord> currentRoleRecordOpt = recordList.stream().filter(r -> r.getAccountRole().equals(request.getRole().getValue())).findFirst();

        currentRoleRecordOpt.ifPresent(r -> {
            switch (OpenAccountRecordStatusEnum.of(r.getStatus())) {
                case AUDITING:
                    throw new BizException(AccountRegisterErrorCode.ACCOUNT_AUDITING);
                case AUDIT_RETURN:
                    throw new BizException(AccountRegisterErrorCode.ACCOUNT_AUDIT_RETURN);
                case AUDIT_REJECT:
                    throw new BizException(AccountRegisterErrorCode.ACCOUNT_AUDIT_REJECT);
                case SUCCESS:
                    throw new BizException(AccountRegisterErrorCode.HAS_REGISTERED_ACCOUNT);
            }
        });
        this.innerCreateRecordValid(request, recordList);
        Optional<OpenAccountRecord> otherRoleRecordOpt = recordList.stream().filter(r -> !r.getAccountRole().equals(request.getRole().getValue())).findFirst();
        // 开户记录
        OpenAccountRecord record = new OpenAccountRecord();
        record.setLastRequestTime(new Date());
        record.setStatus(OpenAccountRecordStatusEnum.PROCESSING.getValue());
        record.setOpenAccountWay(request.getOpenAccountWay().getValue());
        if (currentRoleRecordOpt.isPresent()) {
            // 存在则刷新状态和最后请求时间
            record.setId(currentRoleRecordOpt.get().getId());
            record.setOrgId(request.getOrgId());
        } else {
            // 不存在则初始化记录
            record.setId(snowflakeIdWorker.nextId());
            record.setUserId(request.getUserId());
            record.setAccountNo(String.valueOf(snowflakeIdWorker.nextId()));
            // record.setGroupAccountNo(otherRoleRecordOpt.map(OpenAccountRecord::getGroupAccountNo).orElse(null));
            record.setAccountRole(request.getRole().getValue());
            record.setCreateTime(new Date());
            record.setOrgId(request.getOrgId());
        }
        // 订单
        OpenAccountOrderOperation<T> openAccountOrderOperation = openAccountOrderHandler.get(this.getType());
        OpenAccountRecord openAccountRecord = currentRoleRecordOpt.orElse(record);
        // 如果是集团账户关联，则设置该记录的集团账户编号
        if (request.getOpenAccountWay().getMajor() == OpenAccountMajorWay.GROUP_ACCOUNT_CORRELATE && otherRoleRecordOpt.isPresent()) {
            record.setGroupAccountNo(otherRoleRecordOpt.get().getGroupAccountNo());
            openAccountRecord.setGroupAccountNo(otherRoleRecordOpt.get().getGroupAccountNo());
        }
        T newOrder = openAccountOrderOperation.getNewOrder(request, openAccountRecord);
        if (currentRoleRecordOpt.isPresent()) {
            openAccountRecordMapperExt.updateStatusById(record);
        } else {
            openAccountRecordMapperExt.insertSelective(record);
        }
        openAccountOrderOperation.create(newOrder);
        // 懒猫请求参数
        return openAccountLanMaoHandler.get(this.getType()).generateLanMaoGatewayDTO(request, newOrder);
    }

    protected abstract void innerCreateParamValid(CreateRequest request);

    protected abstract void innerCreateRecordValid(CreateRequest request, List<OpenAccountRecord> recordList);

    @Transactional(isolation = Isolation.READ_COMMITTED)
    public UpdateResponse update(UpdateRequest request) {
        log.info("开户更新###请求, data={}", JSON.toJSONString(request));
        OpenAccountOrderOperation<T> openAccountOrderOperation = openAccountOrderHandler.get(this.getType());
        T order = openAccountOrderOperation.getByRequestNo(request.getRequestNo());
        // 开户订单是否存在
        if (order == null) {
            throw new BizException(AccountRegisterErrorCode.ACCOUNT_REGISTER_ORDER_NOT_EXISTS);
        }
        // 开户订单是否成功
        if (openAccountOrderOperation.isSuccess(order)) {
            return this.getUpdateResponse(OpenAccountRecordStatusEnum.SUCCESS);
        }
        OrderBasicInfo orderBasicInfo = openAccountOrderOperation.getOrderBasicInfo(order);
        // 开户记录是否存在
        OpenAccountRecord record;
        if(order instanceof OpenAccountOrder){
            OpenAccountOrder openAccountOrder = (OpenAccountOrder)order;
            record = openAccountRecordMapperExt.selectByUserIdAndRoleAndOrgIdForUpdate(orderBasicInfo.getUserId(), orderBasicInfo.getRole(),openAccountOrder.getOrgId());
        }else{
            record = openAccountRecordMapperExt.selectByUserIdAndRoleForUpdate(orderBasicInfo.getUserId(), orderBasicInfo.getRole());
        }

        if (record == null) {
            throw new BizException(AccountRegisterErrorCode.ACCOUNT_REGISTER_RECORD_NOT_EXISTS);
        }
        // 开户是否成功
        if (OpenAccountRecordStatusEnum.except(record.getStatus(), OpenAccountRecordStatusEnum.SUCCESS)) {
            return this.getUpdateResponse(OpenAccountRecordStatusEnum.SUCCESS);
        }
        // 开户状态
        OpenAccountRecordStatusEnum recordStatus = this.judgeRecordStatusByAuditStatus(request);
        // 更新开户记录
        OpenAccountRecord updateRecord = new OpenAccountRecord();
        updateRecord.setId(record.getId());
        updateRecord.setStatus(recordStatus.getValue());
        updateRecord.setSuccessTime(recordStatus == OpenAccountRecordStatusEnum.SUCCESS ? new Date() : null); // 成功时间
        updateRecord.setGroupAccountNo(request.getGroupAccountNo()); // 集团账户编号
        boolean recordUpdateResult = openAccountRecordMapperExt.updateStatusById(updateRecord) > 0;
        log.info("开户更新###开户记录更新###data={}, result={}", JSON.toJSONString(updateRecord), recordUpdateResult);
        // 更新订单
        T updateOrder = openAccountOrderOperation.getUpdateOrder(order, request, recordStatus);
        openAccountOrderOperation.update(updateOrder);
        // 开户方式
        OpenAccountWayEnum openAccountWay = OpenAccountWayEnum.of(record.getOpenAccountWay());
        // 审核通过, 修改账户信息
        if (recordStatus == OpenAccountRecordStatusEnum.SUCCESS) {
            AccountInfo accountInfo = this.innerUpdateAccountInfo(record, order, request);
            accountInfo.setIsUpdateBankCard(openAccountWay.getMajor() != OpenAccountMajorWay.GROUP_ACCOUNT_CORRELATE);
            boolean accountUpdateResult;
            if (orderBasicInfo.getIsInitAccount()) {
                accountUpdateResult = accountInfoComposite.save(accountInfo);
            } else {
                accountUpdateResult = accountInfoComposite.update(accountInfo);
            }
            log.info("开户更新###开户记录更新###data={}, result={}", JSON.toJSONString(accountInfo), accountUpdateResult);
            TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
                @Override
                public void afterCommit() {
                    try {
                        if(OrgConstant.NIIWOO_ORG_ID.equals(accountInfo.getOrgId())
                                && AccountTypeEnum.PERSONAL.equals(accountInfo.getAccountType())
                                && UserRoleEnum.INVESTOR.getValue().equals(accountInfo.getRole())){
                            //开通存管，发送MQ消息  -->用户是否新手任务
                            NoviceTaskRecordRequestDTO dto = new NoviceTaskRecordRequestDTO();
                            dto.setUserId(accountInfo.getUserId());
                            dto.setTaskType(NoviceTaskPrizeEnum.OPEN_DEPOSITORY.getCode());
                            rabbitTemplate.convertAndSend(RabbitConstant.Exchange.NOVICE_TASK_RECORD, RabbitConstant.RoutingKey.NOVICE_TASK_RECORD, dto);
                        }
                    } catch (Exception e) {
                        log.error("novice_task_activity开通存管###调用否新手任务###请求失败,userId={}, errorMessage={}", accountInfo.getUserId(), e.getMessage());
                    }
                    try {
                        if(UserRoleEnum.INVESTOR.getValue().equals(accountInfo.getRole())){
                            CallOutTaskAddReqDTO dto = new CallOutTaskAddReqDTO();
                            dto.setUserId(accountInfo.getUserId());
                            dto.setCallOutType((byte) 2);
                            dto.setOpenAccountTime(new Date());
                            log.info("用户userId:{}新手开户外呼消息发送", accountInfo.getUserId());
                            rabbitTemplate.convertAndSend(UserRabbitConstant.Exchange.CALL_OUT_TASK_ADD, UserRabbitConstant.RoutingKey.CALL_OUT_TASK_ADD, JSONObject.toJSONString(dto));
                        }
                    } catch (Exception e) {
                        log.error("新手开户外呼通知神盾,userId={}, errorMessage={}", accountInfo.getUserId(), e.getMessage());
                    }
                }
            });
        }
        // 触发监听事件，用于开户后置处理
        TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
            @Override
            public void afterCommit() {
                super.afterCommit();
                OpenAccountEvent event = new OpenAccountEvent();
                record.setStatus(recordStatus.getValue());
                record.setSuccessTime(updateRecord.getSuccessTime());
                record.setGroupAccountNo(updateRecord.getGroupAccountNo());
                event.setRecord(record);
                event.setErrorCode(request.getErrorCode());
                event.setErrorMessage(request.getErrorMessage());
                openAccountListeners.onNotify(event);
            }
        });
        // 返回响应
        log.info("开户更新###结果, status={}", recordStatus);
        return this.getUpdateResponse(recordStatus);
    }

    protected abstract AccountInfo innerUpdateAccountInfo(OpenAccountRecord record, T order, UpdateRequest request);

    protected void innerUpdateNotifyEvent(OpenAccountRecord record, OpenAccountEvent event) {
    }

    protected UpdateResponse getUpdateResponse(OpenAccountRecordStatusEnum recordStatus) {
        UpdateResponse response = new UpdateResponse();
        response.setRecordStatus(recordStatus);
        return response;
    }

    protected OpenAccountRecordStatusEnum judgeRecordStatusByAuditStatus(UpdateRequest request) {
        if (request.getIsBizSuccess()) {
            switch (request.getAuditStatus()) {
                // 审核通过， 账户相关数据审核通过，可以写库，但不一定表示用户可用（还需要判断账户激活状态）
                case PASSED:
                    return OpenAccountRecordStatusEnum.SUCCESS;
                // 拒绝，暂时针对企业用户
                case REFUSED:
                    return OpenAccountRecordStatusEnum.AUDIT_REJECT;
                // 审核中，暂时针对企业用户
                case AUDIT:
                    return OpenAccountRecordStatusEnum.AUDITING;
                // 退回，暂时针对企业用户
                case BACK:
                    return OpenAccountRecordStatusEnum.AUDIT_RETURN;
                default: {
                    log.error("未知审核类型, accountNo={}, auditStatus={}", request.getAccountNo(), request.getAuditStatus());
                    throw new BizException("SYS9999");
                }
            }
        } else {
            return OpenAccountRecordStatusEnum.FAILURE;
        }
    }

    protected boolean isNiiWooAccount(String orgId){
        return StringUtils.isEmpty(orgId)?false:(orgId.equals(OrgConstant.NIIWOO_ORG_ID));
    }
}
